/************************************************************************
 ** This file is part of the network simulator Shawn.                  **
 ** Copyright (C) 2004-2007 by the SwarmNet (www.swarmnet.de) project  **
 ** Shawn is free software; you can redistribute it and/or modify it   **
 ** under the terms of the BSD License. Refer to the shawn-licence.txt **
 ** file in the root of the Shawn source tree for further details.     **
 ************************************************************************/

#ifdef ENABLE_SHAWNBGU
#include "legacyapps/shawnbgu/shawnbgu_processor.h"
#include "legacyapps/shawnbgu/shawnbgu_message.h"
#include "legacyapps/shawnbgu/shawnbgu_local_topology.cpp"
#include "sys/simulation/simulation_controller.h"
#include "sys/node.h"
#include <iostream>
#include <time.h>

namespace shawnbgu {

	ShawnbguProcessor::
	ShawnbguProcessor(): is_leader_(false)
	{
	}
	// ----------------------------------------------------------------------
	ShawnbguProcessor::
	~ShawnbguProcessor()
	{}

	// ----------------------------------------------------------------------
	void
	ShawnbguProcessor::
	boot(void)
	throw()
	{
		write_int_tag( owner_w(), "leader", owner_w().id() );
		write_bool_tag(owner_w(), "isLeader", false );
		const shawn::SimulationEnvironment& se =
		                     owner().world().simulation_controller().environment();
		      diameter_ = se.optional_int_param( "radius", 3 );

	}
	// ----------------------------------------------------------------------

	const
	ShawnbguLocalTopology&
	ShawnbguProcessor::
	local_topology( void )
	{
		return local_topology_;
	}
	// ----------------------------------------------------------------------
	bool
	ShawnbguProcessor::
	process_message( const ConstMessageHandle& mh )
	throw()
	{
	  DEBUG( logger(), "node No. "
				<<owner().id()
				<<" entering ShawnbguProcessor::process_message");


		const ShawnbguMessage* msg =
		dynamic_cast<const ShawnbguMessage*>( mh.get() );

		if( msg != NULL ) {

			if( owner() != msg->source() ) {



				/* TODO : understand the problem with that code
				 * (see at work() also)

				const ShawnbguMessage* tmp = msg;

				messages_.push(msg);

				 */


				neighbours_data_.push(msg->local_topology());



			}
		}

		return Processor::process_message( mh );
	}
	// ----------------------------------------------------------------------
	void
	ShawnbguProcessor::
	work( void )
	throw()
	{
	  DEBUG( logger(), "node No. "
			<<owner().id()
		    <<" entering ShawnbguProcessor::work");
	  


	  int round = owner_w().simulation_round();
	  /*if ((round == 1)){//&&(owner().id()==1)){

		  //cout<<"round4"<<endl;
		  init();
		  round = round;
		  round = round;

	  }
*/
		init();
		update_local_topology();
		find_leader();
		write_int_tag(owner_w(), "leader", leader_);
		write_bool_tag(owner_w(), "isLeader", is_leader_ );
		ShawnbguMessage* msg = new ShawnbguMessage(local_topology());

		send(msg);
		//send(new ShawnbguMessage(local_topology()));
	}








	void
	ShawnbguProcessor::
	init(void)
	throw()
	{
		int id = owner().id();
		leadership_=id;
		local_topology_.init(diameter_, id,is_leader_,leadership_);
	}

	// ----------------------------------------------------------------------
	void
	ShawnbguProcessor::
	update_local_topology()
	{
		DEBUG( logger(), "node No. "
				<<owner().id()
				<<" entering ShawnbguProcessor::update_local_topology");

		// TODO: implement update_local_topology:
		// adds each proccessor in neighbour topology to the local topology
		//keeping the following conditions;
		//1. the added proccessor distance is one more to his own distance
		//2. the local proccessor doesn't added


		while(neighbours_data_.size()>0)
		{

			ShawnbguLocalTopology neighbour_topology = neighbours_data_.front();

			DEBUG( logger(), "in simulation round No. "
					<< simulation_round()
					<< " node No. "
					<< owner().id()
					<< " worked on message from node No. "
					<< neighbour_topology.owner_id_w()
					<< endl);

			local_topology_.add(neighbour_topology);
			neighbours_data_.pop();

		}

		log_neighbourhood();//INFO log level of the neighbourhood at the end of the round



	}
	// ----------------------------------------------------------------------
		void
		ShawnbguProcessor::
		find_leader()
		{
			int debug_round = owner_w().simulation_round();
			if (debug_round==7){
				DEBUG(logger(), "");
			}

;
			//pair<bool,int> leadership = local_topology_.find_leader();
			pair<int,int> leadership = local_topology_.find_leader();

			leader_=leadership.first;

			leadership_=leadership.second;

			is_leader_ = (leader_==owner_w().id());
		}

	// ----------------------------------------------------------------------
	void
	ShawnbguProcessor::
	log_neighbourhood()
	{
		DEBUG( logger(), "This is the neighbourhood of node No. "
				<<owner().id()
				<<" at the end of round No. "
				<< simulation_round()
		);

		for (int dist=0; dist<diameter_; dist++) {
			if ((simulation_round()==3)&&(owner().id()==0)&&(dist==2)){
						//cout<<"NOW";
				}
			if (local_topology_.nodes_in_dist_exist(dist)) {
				map<int,shawnbguNodeData> nodes = local_topology_.nodes_in_dist(dist);
				map<int,shawnbguNodeData>::iterator nodes_it;

				DEBUG( logger(), "The neighbours in distance "
						<< dist
						<< " are the following nodes: "
				);

				for (nodes_it=nodes.begin(); nodes_it!=nodes.end(); nodes_it++) {
					DEBUG( logger(), "node ID "
							<<nodes_it->second.node_id_
							<<" with parent "
							<< nodes_it->second.parent_id_
					);

				}

			}
		}
	}
	// ----------------------------------------------------------------------
	void
	ShawnbguProcessor::
	write_int_tag( shawn::Node& node, const std::string& tag_name, int value )
	      throw()
	      {

	      shawn::TagHandle tag = node.find_tag_w( tag_name );

	      if ( tag.is_not_null() ){

	         shawn::IntegerTag* intt = dynamic_cast<shawn::IntegerTag*>( tag.get() );


	         if ( intt != NULL ){

	            intt->set_value( value );
	         }
	      }
	      else{

	         shawn::IntegerTag *intt = new shawn::IntegerTag( tag_name, value );
	         node.add_tag( intt );
	      }


	}
	// ----------------------------------------------------------------------
	void
	ShawnbguProcessor::
	write_bool_tag( shawn::Node& node, const std::string& tag_name, bool value )
	throw()
	{

		shawn::TagHandle tag = node.find_tag_w( tag_name );

		if ( tag.is_not_null() ) {

			shawn::BoolTag* boolt = dynamic_cast<shawn::BoolTag*>( tag.get() );
			//shawn::IntegerTag* intt = dynamic_cast<shawn::IntegerTag*>( tag.get() );

			if ( boolt != NULL ) {

				boolt->set_value( value );
			}
		}
		else {

			shawn::BoolTag *boolt = new shawn::BoolTag( tag_name, value );
			node.add_tag( boolt );
		}

	}
		// ----------------------------------------------------------------------

}
#endif
